Framework.Utilities = {}

function Framework.Utilities.URLEncode( s ) 
	local Url = ""
	for i = 1, s:len() do
		local c = s:sub( i, i )
		local a = string.byte( c )
		if( a < 128 ) then
			if( a == 32 || a >= 34 && a <=38 || a == 43 || a == 44 || a == 47 || a >= 58 && a <= 64 || a >= 91 && a <=94 || a == 96 || a >= 123 && a <= 126 ) then
				Url = Url .. "%" .. string.format( "%x", a )
			else
				Url = Url .. c
			end
		end
	end
	return Url
end

function Framework.Utilities.IsIP( ST_String )
	return type(ST_String) == "string" && ST_String:match( "^%d+%.%d+%.%d+%.%d+:?%d*$" ) || false
end

function Framework.Utilities.IsSteamID( ST_String )
	return (type(ST_String) == "string" && (ST_String:match( "^STEAM_%d:%d:%d+$" ) || ST_String:match( "^loopback$" ) || ST_String:match( "^localhost:?%d*$"))) || false
end

function Framework.Utilities.IsUniqueID( AY_Input )
	if( type(AY_Input) == "number" && AY_Input % 1 ) then
		return true -- Might be, anyway.
	elseif( type(AY_Input) == "string" ) then
		local i = tonumber(AY_Input)
		if( i && i % 1 == 0 ) then
			return true
		end
	end
	return false
end

function Framework.Utilities.Round( FL_Float, OP_NU_Decimals )
	OP_NU_Decimals = OP_NU_Decimals or 1
	FL_Float = FL_Float * (10^OP_NU_Decimals)
	return ( ( FL_Float % 1 >= 0.5 && math.ceil( FL_Float ) ) || math.floor( FL_Float ) ) / (10^OP_NU_Decimals)
end

function Framework.Utilities.StringCountInstances( ST_String, ST_Needle )
	local Counter = -1
	local Found = 1
	while( Found ) do
		Counter = Counter + 1
		Found = ST_String:find( ST_Needle, Found ) + 1
	end
	return Counter
end

function Framework.Utilities.StringGetLine( ST_String, NU_Line )
	local Offset = 0
	local Counter = 1
	while( Offset && Counter < NU_Line) do
		Offset = Offset + 1
		Offset = ST_String:find( "\n", Offset )
		Counter = Counter + 1
	end
	if( Offset ) then
		return string.sub( ST_String, Offset, string.find( ST_String, "\n", Offset+1 )-1 )
	end
	return false
end

function Framework.Utilities.FileReadLine( ST_File, NU_Line )
	return Framework.Utilities.StringGetLine( file.Read( ST_File ), NU_Line )
end


local C = string.char
local B = string.byte
local F = math.floor

Framework.Utilities.NumberToChar = string.char
Framework.Utilities.CharToNumber = string.byte

function Framework.Utilities.SteamToUniqueID( ST_String )
	return Framework.Utilities.IsSteamID( ST_String ) && util.CRC( "gm_" .. ST_String .. "_gm" )
end

function Framework.Utilities.IPToNumber( IP_IPAddress )
	local a, b, c, d = IP_IPAddress:match( "^(%d+)%.(%d+)%.(%d+)%.(%d+):?%d*$" )
	return a * 16777216 + b * 65536 + c * 256 + d
end

function Framework.Utilities.NumberToIP( NU_Number )
	return F(NU_Number / 16777216) % 256 .. "." .. F(NU_Number / 65536) % 256 .. "." .. F(NU_Number / 256) % 256 .. "." .. NU_Number % 256
end

function Framework.Utilities.NumberToBinary16( NU_Number )
	return C( F(NU_Number / 256) % 256, NU_Number % 256 )
end

function Framework.Utilities.NumberToBinary32( NU_Number )
	return C( F(NU_Number / 16777216) % 256, F(NU_Number / 65536) % 256, F(NU_Number / 256) % 256, NU_Number % 256 )
end

function Framework.Utilities.Binary32ToNumber( NU_Binary )
	return B(NU_Binary:sub(1,1)) * 16777216 + B(NU_Binary:sub(2,2)) * 65536 + B(NU_Binary:sub(3,3)) * 256 + B(NU_Binary:sub(4,4))
end

function Framework.Utilities.Binary16ToNumber( NU_Binary )
	return B(NU_Binary:sub(1,1)) * 256 + B(NU_Binary:sub(2,2))
end

function Framework.Utilities.BinaryToVector( NU_Binary )
	return Vector( Framework.Utilities.BinaryToFloat( NU_Binary:sub( 1, 4 ) ),
				   Framework.Utilities.BinaryToFloat( NU_Binary:sub( 5, 8 ) ),
				   Framework.Utilities.BinaryToFloat( NU_Binary:sub( 9, 12 ) ) )
end

function Framework.Utilities.BinaryToAngle( NU_Binary )
	return Angle( Framework.Utilities.BinaryToFloat( NU_Binary:sub( 1, 4 ) ),
				   Framework.Utilities.BinaryToFloat( NU_Binary:sub( 5, 8 ) ),
				   Framework.Utilities.BinaryToFloat( NU_Binary:sub( 9, 12 ) ) )
end

function Framework.Utilities.VectorToBinary( AG_VC_Vector )
	return Framework.Utilities.FloatToBinary( AG_VC_Vector.x or AG_VC_Vector.p ) ..
		   Framework.Utilities.FloatToBinary( AG_VC_Vector.y ) ..
		   Framework.Utilities.FloatToBinary( AG_VC_Vector.z or AG_VC_Vector.r )
end
Framework.Utilities.AngleToBinary = Framework.Utilities.VectorToBinary

/*************************************
*** Float conversion code by Overv ***
*************************************/

function Framework.Utilities.BinaryToFloat( NU_Binary )
	local a = B(NU_Binary:sub(1,1))
	local b = B(NU_Binary:sub(2,2))
	local c = B(NU_Binary:sub(3,3))
	local e = B(NU_Binary:sub(4,4))
		
	local s = a * 65536 + b * 256 + c - 8388608
	if ( s > 0 ) then
		return tonumber( "0." .. s ) * 10^e
	else
		return tonumber( "-0." .. math.abs( s ) ) * 10^e
	end
end

function Framework.Utilities.FloatToBinary( FL_Float )
	local Float = FL_Float or 0	
	local neg = Float < 0
	Float = math.abs( Float )

	local e = 0
	if ( Float >= 1 ) then
		while ( Float >= 1 ) do
			Float = Float / 10
			e = e + 1
		end
	else
		while ( Float < 0.1 ) do
			Float = Float * 10
			e = e - 1
		end
	end
	
	local s = tonumber( string.sub( Float, 3, 9 ) )
	if ( neg ) then s = -s end
	s = s + 8388608
	local a = math.modf( s / 65536 ) s = s - a * 65536
	local b = math.modf( s / 256 ) s = s - b * 256
	local c = s
	
	return C( a, b, c, e )
end

local FrameworkTags = {
	{ "Any_", "(any) " },
	{ "String_", "(string) " },
	{ "Table_", "(table) " },
	{ "Number_", "(number) " },
	{ "Float_", "(float) " },
	{ "Vector_", "(vector) "},
	{ "Angle_", "(angle) "},
	{ "Entity_", "(entity) "},
	{ "Player_", "(player) "},
	{ "Function_", "(function) " },
	{ "Flag_", "(flag) " },
	{ "RFilter_", "(recipient filter) " },
	{ "Bool_", "(bool) " },
	{ "Enum_", "(enum) " },
	
	{ "AY_", "(any) " },
	{ "ST_", "(string) " },
	{ "TA_", "(table) " },
	{ "NU_", "(number) " },
	{ "FL_", "(float) " },
	{ "VC_", "(vector) "},
	{ "AG_", "(angle) "},
	{ "ET_", "(entity) "},
	{ "PL_", "(player) "},
	{ "FU_", "(function) " },
	{ "FG_", "(flag) " },
	{ "RF_", "(recipient filter) " },
	{ "UN_", "(unique id) " },
	{ "SI_", "(steam id) " },
	{ "IP_", "(ip address) " },
	{ "BL_", "(bool) " },
	{ "EN_", "(enum) " },
	
	{ "Optional_", "(optional) " },
	{ "OP_", "(optional) " },
	
	{ "%) %(", ", " },
	{ "%.%.%.", "(varargs) ..." }
}

function Framework.Utilities.GetFunctionArguments( FU_Function )
	local Info = debug.getinfo( FU_Function, "nSl" )
	if( Info.short_src == "[C]" ) then return false end
	if( CLIENT && !Info.short_src:find("^addons" ) ) then Info.short_src = "lua/" .. Info.short_src end

	local Source = Framework.Utilities.StringGetLine( file.Read( "../" .. Info.short_src ), Info.linedefined )
	local Arguments = {} 
	for Arg in Source:gmatch( "[%(,]%s+([%a_%.]+)%s-%)-" ) do 
		for _, Tag in pairs( FrameworkTags ) do
			Arg = Arg:gsub( Tag[1], Tag[2] )
		end
		table.insert( Arguments, Arg )
	end
	return Arguments
end

function Framework.Utilities.GetFunctionInfo( FU_Function )
	local Info = debug.getinfo( FU_Function, "nSl" )
	local Name, Line, Arguments, Path = "Unknown", -1, {}, "[C]"
	if( Info.short_src != "[C]" ) then 
		local Source = Framework.Utilities.StringGetLine( file.Read( "../" .. Info.short_src ), Info.linedefined )
		Name = Source:match( "function ([%a%p]+)%(" )
		Path = Info.short_src
		Line = Info.linedefined
		Arguments = Framework.Utilities.GetFunctionArguments( FU_Function )
	end
	return { Path = Path, Name = Name, Line = Line, Arguments = Arguments }
end


local UsingTables = {}

debug.sethook( function(...)
	local Info = debug.getinfo(2)
	for func, v in pairs( UsingTables ) do
		if( func == Info.func ) then
			-- Restore globals
			for k, v in pairs( v ) do
				_G[k] = v[1]
			end
		end
	end
end, "r" )

function Framework.Utilities.Using(t)
	local Func = debug.getinfo(2).func
	UsingTables[Func] = {}
	for k, v in pairs( t ) do
		-- Save the old value
		UsingTables[Func][k] = {_G[k] or nil}
		-- Overwrite
		_G[k] = v
	end
end
